/* Copyright (c) 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.orkut.client.api.samples.greetings.server;

import com.google.appengine.repackaged.com.google.common.base.Function;
import com.google.appengine.repackaged.com.google.common.collect.Collections2;
import com.google.appengine.repackaged.com.google.common.collect.Lists;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.internal.Join;
import com.google.orkut.client.api.ActivityTxFactory;
import com.google.orkut.client.api.CaptchaTxFactory;
import com.google.orkut.client.api.FriendTxFactory;
import com.google.orkut.client.api.GetFriendTx;
import com.google.orkut.client.api.GetProfileTx;
import com.google.orkut.client.api.OrkutError;
import com.google.orkut.client.api.OrkutPerson;
import com.google.orkut.client.api.PostActivityTx;
import com.google.orkut.client.api.ProfileTxFactory;
import com.google.orkut.client.api.ScrapTxFactory;
import com.google.orkut.client.api.WriteScrapTx;
import com.google.orkut.client.api.samples.greetings.client.services.CaptchaInfo;
import com.google.orkut.client.api.samples.greetings.client.services.PhotoInfo;
import com.google.orkut.client.api.samples.greetings.client.services.UserInfo;
import com.google.orkut.client.api.samples.greetings.client.services.UserService;
import com.google.orkut.client.api.samples.greetings.server.AuthorizedChannel.Factory;
import com.google.orkut.client.api.samples.greetings.server.db.GreetingsDb;
import com.google.orkut.client.api.samples.greetings.server.servlets.CaptchaServlet;
import com.google.orkut.client.api.samples.greetings.shared.AuthorizationException;
import com.google.orkut.client.api.samples.greetings.shared.CommonConstants;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * Implements {@link UserService}.
 *
 * @author Shishir Birmiwal
 */
@Singleton
public class UserServiceImpl extends RemoteServiceServlet implements UserService {
  private static final int MAX_FRIENDS = 1000;
  private final ActivityTxFactory activityFactory;
  private final CachedConfig cachedConfig;
  private final CaptchaTxFactory captchaFactory;
  private final Factory factory;
  private final FriendTxFactory friendFactory;
  private final ProfileTxFactory profileFactory;
  private final ScrapTxFactory scrapFactory;
  private final GreetingsDb db;

  @Inject
  public UserServiceImpl(
      ActivityTxFactory activityFactory,
      AuthorizedChannel.Factory factory,
      CaptchaTxFactory captchaFactory,
      CachedConfig cachedConfig,
      GreetingsDb db,
      ProfileTxFactory profileFactory,
      FriendTxFactory friendFactory,
      ScrapTxFactory scrapFactory) {
    this.activityFactory = activityFactory;
    this.factory = factory;
    this.captchaFactory = captchaFactory;
    this.cachedConfig = cachedConfig;
    this.db = db;
    this.profileFactory = profileFactory;
    this.friendFactory = friendFactory;
    this.scrapFactory = scrapFactory;
  }

  private UserInfo convertToUserInfo(OrkutPerson person) {
    UserInfo userInfo = new UserInfo();
    userInfo.thumbnailUrl = person.getThumbnailUrl();
    userInfo.userName = person.getDisplayName();
    userInfo.id = person.getId();
    return userInfo;
  }

  private String getBody(String postToken, PhotoInfo photo, ArrayList<String> toUserIds)
  throws AuthorizationException {
    List<GetProfileTx> getProfile = Lists.newArrayList();
    GetProfileTx selfProfile = profileFactory.getSelfProfile();
    for (String id : toUserIds) {
      getProfile.add(profileFactory.getProfileOf(id).alsoGetProfileUrl().alsoGetName());
    }
    AuthorizedChannel channel = factory.create(postToken, getRequest());
    channel.add(selfProfile);
    for (GetProfileTx tx : getProfile) {
      channel.add(tx);
    }
    try {
      channel.run();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    String to = " to: " + Join.join(", " , Collections2.transform(getProfile, new Function<GetProfileTx, String>() {
      @Override
      public String apply(GetProfileTx getProfileTx) {
        if (getProfileTx.hasError()) {
          return "";
        }
        String msg = "<a href=\"http://www.orkut.com" + getProfileTx.getProfile().getProfileUrl() + "\">"
            + getProfileTx.getProfile().getGivenName() + "</a>";
        return msg;
      }
    }));
    // TODO(birmiwal): Localize this!
    String givenName = selfProfile.hasError() ? "" : selfProfile.getProfile().getGivenName();
    return String.format("<table border=0 padding=3><tr>" +
    		"<td><a href=\"%s\" target=_blank><img src=\"%s\" height=60 /></a></td>" +
    		"<td width=5 />" +
    		"<td>%s scrapped a photo %s<br/><br/>" +
    		"scrap photos and greetings using <a href=\"%s\">%s</a></td>" +
    		"</tr></table>",
    		getHostname(), photo.thumbnailUrl,
    		givenName, to,
    		getHostname(), cachedConfig.getConfig().getSiteName());
  }

  private String getCaptchaUrl(OrkutError error, String postToken) {
    return "/captcha?"
        + CommonConstants.SESSION_TOKEN_PARAM + "=" + URLEncoder.encode(postToken) + "&"
        + CaptchaServlet.CAPTCHA_IMG_PARAM + "=" + error.captchaUrl();
  }

  public String getFooterMessage() {
    String host = getHostname();
    return String.format("%s (%s)", cachedConfig.getConfig().getSiteName(), host);
  }

  @Override
  public ArrayList<UserInfo> getFriendsOf(String postToken, String userId)
  throws AuthorizationException {
    AuthorizedChannel channel = factory.create(postToken, getRequest());
    GetFriendTx getFriendTx = friendFactory.getFriendsOf(userId);
    getFriendTx.setCount(MAX_FRIENDS);

    try {
      channel.add(getFriendTx).run();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    ArrayList<UserInfo> users = new ArrayList<UserInfo>();
    for (int i = 0; i < getFriendTx.getFriendsCount(); i++) {
      users.add(convertToUserInfo(getFriendTx.getFriend(i)));
    }
    return users;
  }

  private String getHostname() {
    HttpServletRequest request = getThreadLocalRequest();
    String host = "http://" + request.getHeader("Host");
    return host;
  }

  private String getHtmlMessage(PhotoInfo photoInfo, boolean useSmallerImage, String msg) {
    return msg + " "
//      + "<img src=\""
//      + (useSmallerImage ? photoInfo.thumbnailUrl : photoInfo.imageUrl)
        + CommonConstants.getImageUrlServer(photoInfo, useSmallerImage)
//      + "\" width=534 />"
      + "\n" + getFooterMessage();
  }

  @Override
  public UserInfo getLoggedInUserInfo(String postToken) throws AuthorizationException {
    AuthorizedChannel channel = factory.create(postToken, getRequest());
    GetProfileTx getProfileTx = profileFactory.getSelfProfile();
    try {
      channel.add(getProfileTx).run();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    return convertToUserInfo(getProfileTx.getProfile());
  }

  private HttpServletRequest getRequest() {
    return getThreadLocalRequest();
  }

  private String getTitle() {
    return "scrapped a photo using <a href=\"" + getHostname() + "\">" + cachedConfig.getConfig().getSiteName() + "</a>";
  }

  @Override
  public UserInfo getUserInfo(String postToken, String userId) throws AuthorizationException {
    AuthorizedChannel channel = factory.create(postToken, getRequest());
    GetProfileTx getProfileTx = profileFactory.getProfileOf(userId);
    try {
      channel.add(getProfileTx).run();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    return convertToUserInfo(getProfileTx.getProfile());
  }

  @Override
  public boolean logoutUser(String postToken) throws AuthorizationException {
    HttpServletRequest request = getRequest();
    AuthorizedChannel channel = factory.create(postToken, request);
    db.removeCredentials(Cookies.getCookieValueFor(request, CommonConstants.COOKIE));
    Cookie cookie = new Cookie(CommonConstants.COOKIE, "");
    cookie.setMaxAge(0);
    getThreadLocalResponse().addCookie(cookie);
    return true;
  }

  @Override
  public CaptchaInfo scrapUser(String postToken, ArrayList<String> toUserIds,
      PhotoInfo photo, boolean useSmallerImage, boolean postUpdate,
      String msg, String captchaToken, String captchaAnswer)
  throws AuthorizationException {
    AuthorizedChannel channel = factory.create(postToken, getRequest());
    WriteScrapTx writeScrapTx = null;
    String message = getHtmlMessage(photo, useSmallerImage, msg);
    if (captchaToken != null && captchaToken.trim().length() > 0) {
      channel.add(captchaFactory.answerCaptcha(captchaToken, captchaAnswer));
      if (postUpdate) {
        PostActivityTx postActivityTx = activityFactory.postActivity(getTitle(),
            getBody(postToken, photo, toUserIds));
        channel.add(postActivityTx);
      }
    }
    for (String userId : toUserIds) {
      WriteScrapTx writeScrap = scrapFactory.writeScrap(userId, message);
      channel.add(writeScrap);
      if (writeScrapTx == null) {
        writeScrapTx = writeScrap;
      }
    }

    try {
      channel.run();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    OrkutError error = writeScrapTx.getError();
    if (error != null && error.isCaptchaError()) {
      return new CaptchaInfo(getCaptchaUrl(error, postToken), error.captchaToken());
    }
    return null;
  }
}
